/*
 * qrencode - QR Code encoder
 *
 * Binary sequence class.
 * Copyright (C) 2006-2011 Kentaro Fukuchi <kentaro@fukuchi.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "bitstream.h"

BitStream *BitStream_new(void)
{
    BitStream *bstream;

    bstream = (BitStream *)malloc(sizeof(BitStream));
    if(bstream == NULL) return NULL;

    bstream->length = 0;
    bstream->data = NULL;

    return bstream;
}

static int BitStream_allocate(BitStream *bstream, int length)
{
    unsigned char *data;

    if(bstream == NULL) {
        return -1;
    }

    data = (unsigned char *)malloc(length);
    if(data == NULL) {
        return -1;
    }

    if(bstream->data) {
        free(bstream->data);
    }
    bstream->length = length;
    bstream->data = data;

    return 0;
}

static BitStream *BitStream_newFromNum(int bits, unsigned int num)
{
    unsigned int mask;
    int i;
    unsigned char *p;
    BitStream *bstream;

    bstream = BitStream_new();
    if(bstream == NULL) return NULL;

    if(BitStream_allocate(bstream, bits)) {
        BitStream_free(bstream);
        return NULL;
    }

    p = bstream->data;
    mask = 1 << (bits - 1);
    for(i=0; i<bits; i++) {
        if(num & mask) {
            *p = 1;
        } else {
            *p = 0;
        }
        p++;
        mask = mask >> 1;
    }

    return bstream;
}

static BitStream *BitStream_newFromBytes(int size, unsigned char *data)
{
    unsigned char mask;
    int i, j;
    unsigned char *p;
    BitStream *bstream;

    bstream = BitStream_new();
    if(bstream == NULL) return NULL;

    if(BitStream_allocate(bstream, size * 8)) {
        BitStream_free(bstream);
        return NULL;
    }

    p = bstream->data;
    for(i=0; i<size; i++) {
        mask = 0x80;
        for(j=0; j<8; j++) {
            if(data[i] & mask) {
                *p = 1;
            } else {
                *p = 0;
            }
            p++;
            mask = mask >> 1;
        }
    }

    return bstream;
}

int BitStream_append(BitStream *bstream, BitStream *arg)
{
    unsigned char *data;

    if(arg == NULL) {
        return -1;
    }
    if(arg->length == 0) {
        return 0;
    }
    if(bstream->length == 0) {
        if(BitStream_allocate(bstream, arg->length)) {
            return -1;
        }
        memcpy(bstream->data, arg->data, arg->length);
        return 0;
    }

    data = (unsigned char *)malloc(bstream->length + arg->length);
    if(data == NULL) {
        return -1;
    }
    memcpy(data, bstream->data, bstream->length);
    memcpy(data + bstream->length, arg->data, arg->length);

    free(bstream->data);
    bstream->length += arg->length;
    bstream->data = data;

    return 0;
}

int BitStream_appendNum(BitStream *bstream, int bits, unsigned int num)
{
    BitStream *b;
    int ret;

    if(bits == 0) return 0;

    b = BitStream_newFromNum(bits, num);
    if(b == NULL) return -1;

    ret = BitStream_append(bstream, b);
    BitStream_free(b);

    return ret;
}

int BitStream_appendBytes(BitStream *bstream, int size, unsigned char *data)
{
    BitStream *b;
    int ret;

    if(size == 0) return 0;

    b = BitStream_newFromBytes(size, data);
    if(b == NULL) return -1;

    ret = BitStream_append(bstream, b);
    BitStream_free(b);

    return ret;
}

unsigned char *BitStream_toByte(BitStream *bstream)
{
    int i, j, size, bytes;
    unsigned char *data, v;
    unsigned char *p;

    size = BitStream_size(bstream);
    if(size == 0) {
        return NULL;
    }
    data = (unsigned char *)malloc((size + 7) / 8);
    if(data == NULL) {
        return NULL;
    }

    bytes = size  / 8;

    p = bstream->data;
    for(i=0; i<bytes; i++) {
        v = 0;
        for(j=0; j<8; j++) {
            v = v << 1;
            v |= *p;
            p++;
        }
        data[i] = v;
    }
    if(size & 7) {
        v = 0;
        for(j=0; j<(size & 7); j++) {
            v = v << 1;
            v |= *p;
            p++;
        }
        data[bytes] = v;
    }

    return data;
}

void BitStream_free(BitStream *bstream)
{
    if(bstream != NULL) {
        free(bstream->data);
        free(bstream);
    }
}
